package app.itgungnir.compose.wan.widgets

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Upcoming
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged

@Composable
fun <T : Any> WanPagingList(
    modifier: Modifier = Modifier,
    lazyListState: LazyListState = rememberLazyListState(),
    pagingItems: LazyPagingItems<T>,
    onRefresh: () -> Unit = {},
    content: LazyListScope.() -> Unit
) {
    WanSwipeRefresh(modifier = modifier, pagingItems = pagingItems, onRefresh = onRefresh) {
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            state = lazyListState
        ) {
            content()
            item { WanPagingPlaceholders(pagingItems = pagingItems) }
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun <T : Any> WanPagingGrid(
    modifier: Modifier = Modifier,
    lazyListState: LazyListState = rememberLazyListState(),
    pagingItems: LazyPagingItems<T>,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    onRefresh: () -> Unit = {},
    content: LazyGridScope.() -> Unit
) {
    WanSwipeRefresh(modifier = modifier, pagingItems = pagingItems, onRefresh = onRefresh) {
        LazyVerticalGrid(cells = GridCells.Fixed(2), state = lazyListState, contentPadding = contentPadding) {
            content()
            item { WanPagingPlaceholders(pagingItems = pagingItems) }
        }
    }
}

@Composable
private fun <T : Any> WanSwipeRefresh(
    modifier: Modifier = Modifier,
    pagingItems: LazyPagingItems<T>,
    onRefresh: () -> Unit = {},
    content: @Composable () -> Unit
) {
    val refreshingState = rememberSwipeRefreshState(isRefreshing = false)
    SwipeRefresh(
        modifier = modifier.background(MaterialTheme.colors.surface),
        state = refreshingState,
        onRefresh = {
            pagingItems.refresh()
            onRefresh.invoke()
        },
        indicator = { state, trigger ->
            SwipeRefreshIndicator(
                state = state,
                refreshTriggerDistance = trigger,
                backgroundColor = MaterialTheme.colors.background,
                contentColor = MaterialTheme.colors.primary
            )
        }
    ) {
        refreshingState.isRefreshing = pagingItems.loadState.refresh is LoadState.Loading
        content()
    }
}

@Composable
private fun <T : Any> WanPagingPlaceholders(pagingItems: LazyPagingItems<T>) {
    when {
        pagingItems.loadState.refresh is LoadState.Error -> when (pagingItems.itemCount <= 0) {
            true -> ErrorPage { pagingItems.retry() }
            else -> LoadErrorItem { pagingItems.retry() }
        }
        pagingItems.loadState.append is LoadState.Error -> LoadErrorItem { pagingItems.retry() }
        pagingItems.loadState.append is LoadState.Loading -> LoadingItem()
        pagingItems.loadState.append is LoadState.NotLoading -> NoMoreItem()
    }
}

@Composable
private fun LoadingItem() {
    CircularProgressIndicator(modifier = Modifier.padding(10.dp))
}

@Composable
fun NoMoreItem() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 10.dp)
            .padding(vertical = 15.dp),
        horizontalArrangement = Arrangement.Center
    ) {
        Text(
            text = "没有更多数据了～",
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F),
            style = MaterialTheme.typography.subtitle2
        )
    }
}

@Composable
private fun LoadErrorItem(retryCallback: () -> Unit) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 10.dp)
            .clickable { retryCallback.invoke() }
            .padding(vertical = 15.dp),
        horizontalArrangement = Arrangement.Center
    ) {
        Text(
            text = "数据加载失败，点击重试",
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F),
            style = MaterialTheme.typography.subtitle2
        )
    }
}

@Composable
fun ErrorPage(
    icon: ImageVector = Icons.Outlined.Upcoming,
    title: String = "内容列表为空，请稍候重试",
    buttonText: String = "重新获取",
    callback: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 100.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Icon(
            modifier = Modifier.size(100.dp),
            imageVector = icon,
            contentDescription = "",
            tint = MaterialTheme.colors.primary
        )
        Spacer(modifier = Modifier.height(20.dp))
        Text(text = title, style = MaterialTheme.typography.body1)
        Spacer(modifier = Modifier.height(5.dp))
        WanTextButton(text = buttonText) { callback.invoke() }
    }
}

@Composable
fun LazyListState.isItemVisible(key: Any?): State<Boolean> {
    val isVisible = remember {
        mutableStateOf(layoutInfo.visibleItemsInfo.firstOrNull { it.key == key } != null)
    }
    LaunchedEffect(this, key) {
        snapshotFlow {
            layoutInfo.visibleItemsInfo.firstOrNull { it.key == key } != null
        }.distinctUntilChanged().collect {
            isVisible.value = it
        }
    }
    return isVisible
}
